<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js DEM地形渲染器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            color: #333;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            overflow-x: hidden;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        header {
            text-align: center;
            padding: 20px 0;
            margin-bottom: 30px;
        }
        h1 {
            font-size: 2.5rem;
            color: #2c3e50;
            margin-bottom: 10px;
        }
        .subtitle {
            color: #7f8c8d;
            font-size: 1.2rem;
        }
        .content {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-bottom: 30px;
        }
        .canvas-container {
            flex: 1;
            min-width: 300px;
            height: 500px;
            background: #000;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            position: relative;
        }
        #renderCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }
        .controls {
            flex: 1;
            min-width: 300px;
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            max-height: 500px;
            overflow-y: auto;
        }
        .control-group {
            margin-bottom: 20px;
        }
        .control-title {
            font-weight: 600;
            margin-bottom: 10px;
            color: #2c3e50;
            border-bottom: 1px solid #eee;
            padding-bottom: 5px;
        }
        .slider-container {
            display: flex;
            align-items: center;
            margin-bottom: 10px;
        }
        .slider-container label {
            width: 120px;
            font-size: 0.9em;
        }
        .slider-container input {
            flex: 1;
        }
        .slider-value {
            width: 50px;
            text-align: right;
            margin-left: 10px;
            font-size: 0.9em;
        }
        .color-legend {
            height: 20px;
            background: linear-gradient(to right, blue, cyan, green, yellow, orange, red);
            border-radius: 3px;
            margin: 10px 0;
        }
        .legend-labels {
            display: flex;
            justify-content: space-between;
            font-size: 0.8em;
            color: #7f8c8d;
        }
        .file-input {
            margin-bottom: 20px;
        }
        .file-input input {
            width: 100%;
            padding: 10px;
            border: 2px dashed #ccc;
            border-radius: 5px;
            background: #f8f9fa;
        }
        .button {
            background: #3498db;
            color: white;
            border: none;
            padding: 12px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 1em;
            width: 100%;
            margin-top: 10px;
            transition: background 0.3s;
        }
        .button:hover {
            background: #2980b9;
        }
        .button:disabled {
            background: #95a5a6;
            cursor: not-allowed;
        }
        .instructions {
            background: #e3f2fd;
            padding: 15px;
            border-radius: 5px;
            font-size: 0.9em;
            margin-top: 20px;
        }
        .instructions h3 {
            margin-top: 0;
            color: #1565c0;
        }
        .instructions ul {
            padding-left: 20px;
            margin-bottom: 0;
        }
        .instructions li {
            margin-bottom: 5px;
        }
        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 20px;
            border-radius: 5px;
            display: none;
            z-index: 100;
        }
        .progress-bar {
            width: 100%;
            background: #ddd;
            border-radius: 5px;
            margin-top: 10px;
            overflow: hidden;
        }
        .progress {
            height: 10px;
            background: #3498db;
            width: 0%;
            transition: width 0.3s;
        }
        footer {
            text-align: center;
            padding: 20px;
            color: #7f8c8d;
            font-size: 0.9em;
        }
        .error {
            color: #e74c3c;
            margin-top: 10px;
            font-size: 0.9em;
            display: none;
        }
        @media (max-width: 768px) {
            .content {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Three.js DEM地形渲染器</h1>
            <div class="subtitle">优化版本 - 避免堆栈溢出错误</div>
        </header>
        
        <div class="content">
            <div class="canvas-container">
                <canvas id="renderCanvas"></canvas>
                <div class="loading" id="loadingIndicator">
                    <div>正在处理DEM数据...</div>
                    <div class="progress-bar">
                        <div class="progress" id="progressBar"></div>
                    </div>
                    <div id="progressText">0%</div>
                </div>
            </div>
            
            <div class="controls">
                <div class="control-group">
                    <div class="control-title">文件输入</div>
                    <div class="file-input">
                        <input type="file" id="demFileInput" accept=".tif,.tiff">
                        <p style="font-size: 0.8em; margin-top: 5px; color: #7f8c8d;">支持TIFF格式的DEM文件</p>
                    </div>
                    <button id="renderButton" class="button">渲染地形</button>
                    <button id="loadExample" class="button">加载示例数据</button>
                    <div id="errorMessage" class="error"></div>
                </div>
                
                <div class="control-group">
                    <div class="control-title">地形设置</div>
                    <div class="slider-container">
                        <label for="heightScale">高度比例:</label>
                        <input type="range" id="heightScale" min="0.1" max="2" step="0.1" value="0.5">
                        <span class="slider-value" id="heightScaleValue">0.5</span>
                    </div>
                    <div class="slider-container">
                        <label for="resolution">简化比例:</label>
                        <input type="range" id="resolution" min="1" max="10" step="1" value="3">
                        <span class="slider-value" id="resolutionValue">3</span>
                    </div>
                </div>
                
                <div class="control-group">
                    <div class="control-title">颜色映射</div>
                    <div class="color-legend"></div>
                    <div class="legend-labels">
                        <span>低海拔</span>
                        <span>高海拔</span>
                    </div>
                </div>
                
                <div class="control-group">
                    <div class="control-title">场景控制</div>
                    <div class="slider-container">
                        <label for="rotationSpeed">旋转速度:</label>
                        <input type="range" id="rotationSpeed" min="0" max="1" step="0.1" value="0.3">
                        <span class="slider-value" id="rotationSpeedValue">0.3</span>
                    </div>
                    <button id="resetCamera" class="button">重置相机</button>
                    <button id="toggleRotation" class="button">暂停旋转</button>
                </div>
                
                <div class="instructions">
                    <h3>使用说明</h3>
                    <ul>
                        <li>上传DEM TIFF文件并点击"渲染地形"</li>
                        <li>或者直接点击"加载示例数据"使用示例地形</li>
                        <li>使用"简化比例"减少数据量，避免堆栈溢出</li>
                        <li>使用鼠标拖拽来旋转视角</li>
                        <li>使用鼠标滚轮来缩放</li>
                        <li>调整高度比例来夸大或减小地形起伏</li>
                    </ul>
                </div>
            </div>
        </div>
        
        <footer>
            <p>Three.js DEM地形渲染器 &copy; 2023 | 优化版本避免堆栈溢出</p>
        </footer>
    </div>

    <!-- 引入Three.js库 -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>

    <script>
        // 全局变量
        let scene, camera, renderer, controls;
        let terrainMesh = null;
        let heightScale = 0.5;
        let rotationSpeed = 0.3;
        let isRotating = true;
        let isProcessing = false;

        // 初始化Three.js场景
        function initThreeJS() {
            // 创建场景
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xaaaaaa);
            
            // 创建相机
            const container = document.querySelector('.canvas-container');
            camera = new THREE.PerspectiveCamera(60, container.offsetWidth / container.offsetHeight, 0.1, 1000);
            camera.position.set(0, 2, 3);
            
            // 创建渲染器
            renderer = new THREE.WebGLRenderer({ 
                canvas: document.getElementById('renderCanvas'), 
                antialias: true,
                powerPreference: "high-performance"
            });
            renderer.setSize(container.offsetWidth, container.offsetHeight);
            
            // 添加轨道控制
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.05;
            
            // 添加环境光和方向光
            const ambientLight = new THREE.AmbientLight(0x404040);
            scene.add(ambientLight);
            
            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
            directionalLight.position.set(1, 1, 1);
            scene.add(directionalLight);
            
            // 添加网格辅助
            const gridHelper = new THREE.GridHelper(10, 10);
            scene.add(gridHelper);
            
            // 添加坐标轴辅助
            const axesHelper = new THREE.AxesHelper(2);
            scene.add(axesHelper);
            
            // 监听窗口大小变化
            window.addEventListener('resize', onWindowResize);
            
            // 开始动画循环
            animate();
        }
        
        // 窗口大小变化处理
        function onWindowResize() {
            const container = document.querySelector('.canvas-container');
            camera.aspect = container.offsetWidth / container.offsetHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(container.offsetWidth, container.offsetHeight);
        }
        
        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            
            if (isRotating && terrainMesh && !isProcessing) {
                terrainMesh.rotation.y += rotationSpeed * 0.01;
            }
            
            controls.update();
            renderer.render(scene, camera);
        }
        
        // 显示错误信息
        function showError(message) {
            const errorElement = document.getElementById('errorMessage');
            errorElement.textContent = message;
            errorElement.style.display = 'block';
            setTimeout(() => {
                errorElement.style.display = 'none';
            }, 5000);
        }
        
        // 处理高程数据并创建地形网格 - 优化版本
        function processElevationData(data, width, height) {
            isProcessing = true;
            document.getElementById('loadingIndicator').style.display = 'block';
            
            // 移除现有的地形
            if (terrainMesh) {
                scene.remove(terrainMesh);
                if (terrainMesh.geometry) terrainMesh.geometry.dispose();
                if (terrainMesh.material) terrainMesh.material.dispose();
                terrainMesh = null;
            }
            
            try {
                // 获取简化比例
                const resolutionScale = parseInt(document.getElementById('resolution').value);
                
                // 计算简化后的尺寸
                const simplifiedWidth = Math.max(2, Math.floor(width / resolutionScale));
                const simplifiedHeight = Math.max(2, Math.floor(height / resolutionScale));
                
                // 创建简化后的数据数组
                const simplifiedData = new Float32Array(simplifiedWidth * simplifiedHeight);
                
                // 进度更新
                const totalSteps = simplifiedHeight;
                let currentStep = 0;
                
                // 简化数据 - 使用平均值采样
                for (let y = 0; y < simplifiedHeight; y++) {
                    for (let x = 0; x < simplifiedWidth; x++) {
                        // 计算原始数据中的对应区域
                        const startX = Math.floor(x * width / simplifiedWidth);
                        const startY = Math.floor(y * height / simplifiedHeight);
                        const endX = Math.floor((x + 1) * width / simplifiedWidth);
                        const endY = Math.floor((y + 1) * height / simplifiedHeight);
                        
                        // 计算区域内的平均值
                        let sum = 0;
                        let count = 0;
                        
                        for (let sy = startY; sy < endY; sy++) {
                            for (let sx = startX; sx < endX; sx++) {
                                const index = sy * width + sx;
                                if (index < data.length) {
                                    sum += data[index];
                                    count++;
                                }
                            }
                        }
                        
                        simplifiedData[y * simplifiedWidth + x] = count > 0 ? sum / count : 0;
                    }
                    
                    // 更新进度
                    currentStep++;
                    const progress = Math.round((currentStep / totalSteps) * 100);
                    document.getElementById('progressBar').style.width = progress + '%';
                    document.getElementById('progressText').textContent = progress + '%';
                }
                
                // 创建平面几何体 - 使用简化后的尺寸
                const geometry = new THREE.PlaneGeometry(
                    5, 
                    5, 
                    simplifiedWidth - 1, 
                    simplifiedHeight - 1
                );
                
                // 应用高程数据到顶点
                const vertices = geometry.attributes.position.array;
                const maxElevation = Math.max(...simplifiedData);
                const minElevation = Math.min(...simplifiedData);
                
                // 避免除零错误
                const elevationRange = Math.max(0.001, maxElevation - minElevation);
                
                for (let i = 0, j = 0; i < vertices.length; i += 3, j++) {
                    if (j < simplifiedData.length) {
                        // 应用高度比例
                        vertices[i + 2] = (simplifiedData[j] - minElevation) / elevationRange * heightScale;
                    }
                }
                
                // 更新法线
                geometry.computeVertexNormals();
                
                // 创建基于高程的颜色
                const colors = [];
                for (let i = 0, j = 0; i < vertices.length; i += 3, j++) {
                    if (j < simplifiedData.length) {
                        const elevation = (simplifiedData[j] - minElevation) / elevationRange;
                        const color = new THREE.Color();
                        color.setHSL(0.7 * (1 - elevation), 1, 0.5 * (0.5 + elevation));
                        colors.push(color.r, color.g, color.b);
                    }
                }
                
                geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
                
                // 创建材质
                const material = new THREE.MeshPhongMaterial({
                    vertexColors: true,
                    side: THREE.DoubleSide,
                    flatShading: false
                });
                
                // 创建网格
                terrainMesh = new THREE.Mesh(geometry, material);
                terrainMesh.rotation.x = -Math.PI / 2;
                scene.add(terrainMesh);
                
                // 调整相机位置
                camera.position.set(0, 2, 3);
                controls.reset();
                
                console.log(`DEM数据处理完成: 原始 ${width}x${height}, 简化后 ${simplifiedWidth}x${simplifiedHeight}`);
                console.log(`高程范围: ${minElevation.toFixed(2)} 到 ${maxElevation.toFixed(2)}`);
                
            } catch (error) {
                console.error('处理高程数据时出错:', error);
                showError('处理数据时出错: ' + error.message);
            } finally {
                document.getElementById('loadingIndicator').style.display = 'none';
                isProcessing = false;
            }
        }
        
        // 创建示例地形（在没有上传文件时显示）
        function createExampleTerrain() {
            isProcessing = true;
            document.getElementById('loadingIndicator').style.display = 'block';
            
            // 移除现有的地形
            if (terrainMesh) {
                scene.remove(terrainMesh);
                if (terrainMesh.geometry) terrainMesh.geometry.dispose();
                if (terrainMesh.material) terrainMesh.material.dispose();
                terrainMesh = null;
            }
            
            try {
                const width = 100;
                const height = 100;
                const data = new Float32Array(width * height);
                
                // 生成一些随机地形数据
                for (let y = 0; y < height; y++) {
                    for (let x = 0; x < width; x++) {
                        // 使用多种频率的噪声创建更真实的地形
                        const value = 0.7 * Math.sin(x / 20) * Math.cos(y / 20) + 
                                    0.3 * Math.sin(x / 5) * Math.cos(y / 5) +
                                    0.1 * Math.sin(x / 2) * Math.cos(y / 2) +
                                    0.05 * (Math.random() - 0.5);
                        data[y * width + x] = value;
                    }
                    
                    // 更新进度
                    const progress = Math.round((y / height) * 100);
                    document.getElementById('progressBar').style.width = progress + '%';
                    document.getElementById('progressText').textContent = progress + '%';
                }
                
                processElevationData(data, width, height);
            } catch (error) {
                console.error('创建示例地形时出错:', error);
                showError('创建示例地形时出错: ' + error.message);
            }
        }
        
        // 模拟加载TIFF文件（实际项目中应使用GeoTIFF.js）
        function loadDEMFromGeoServer(file) {
            isProcessing = true;
            document.getElementById('loadingIndicator').style.display = 'block';
            
            // 这里模拟一个异步加载和处理过程
            setTimeout(() => {
                try {
                    // 在实际项目中，这里应该使用GeoTIFF.js解析TIFF文件
                    // 以下代码仅用于演示目的
                    
                    // 创建一个模拟的DEM数据
                    const width = 200;
                    const height = 200;
                    const data = new Float32Array(width * height);
                    
                    // 生成一些随机地形数据
                    for (let y = 0; y < height; y++) {
                        for (let x = 0; x < width; x++) {
                            // 使用多种频率的噪声创建更真实的地形
                            const value = 0.6 * Math.sin(x / 30) * Math.cos(y / 30) + 
                                        0.3 * Math.sin(x / 10) * Math.cos(y / 10) +
                                        0.1 * Math.sin(x / 3) * Math.cos(y / 3) +
                                        0.05 * (Math.random() - 0.5);
                            data[y * width + x] = value;
                        }
                        
                        // 更新进度
                        const progress = Math.round((y / height) * 100);
                        document.getElementById('progressBar').style.width = progress + '%';
                        document.getElementById('progressText').textContent = progress + '%';
                    }
                    
                    processElevationData(data, width, height);
                } catch (error) {
                    console.error('加载DEM文件时出错:', error);
                    showError('加载DEM文件时出错: ' + error.message);
                    document.getElementById('loadingIndicator').style.display = 'none';
                    isProcessing = false;
                }
            }, 1000);
        }
        
        // 初始化UI事件
        function initUI() {
            // 文件选择事件
            document.getElementById('renderButton').addEventListener('click', () => {
                if (isProcessing) return;
                
                const fileInput = document.getElementById('demFileInput');
                if (fileInput.files.length > 0) {
                    loadDEMFromGeoServer(fileInput.files[0]);
                } else {
                    showError('请先选择一个DEM TIFF文件');
                }
            });
            
            // 加载示例按钮事件
            document.getElementById('loadExample').addEventListener('click', () => {
                if (isProcessing) return;
                createExampleTerrain();
            });
            
            // 高度比例滑块事件
            const heightScaleSlider = document.getElementById('heightScale');
            const heightScaleValue = document.getElementById('heightScaleValue');
            
            heightScaleSlider.addEventListener('input', (e) => {
                heightScale = parseFloat(e.target.value);
                heightScaleValue.textContent = heightScale.toFixed(1);
                
                // 如果已有地形，重新应用高度比例
                if (terrainMesh && !isProcessing) {
                    const vertices = terrainMesh.geometry.attributes.position.array;
                    const originalVertices = terrainMesh.geometry.attributes.originalPosition?.array || vertices.slice();
                    
                    // 保存原始顶点数据（如果尚未保存）
                    if (!terrainMesh.geometry.attributes.originalPosition) {
                        terrainMesh.geometry.setAttribute('originalPosition', 
                            new THREE.Float32BufferAttribute(originalVertices.slice(), 3));
                    }
                    
                    // 应用新的高度比例
                    for (let i = 0; i < vertices.length; i += 3) {
                        const originalZ = originalVertices[i + 2];
                        vertices[i + 2] = originalZ * (heightScale / 0.5); // 0.5是初始值
                    }
                    
                    terrainMesh.geometry.attributes.position.needsUpdate = true;
                    terrainMesh.geometry.computeVertexNormals();
                }
            });
            
            // 简化比例滑块事件
            const resolutionSlider = document.getElementById('resolution');
            const resolutionValue = document.getElementById('resolutionValue');
            
            resolutionSlider.addEventListener('input', (e) => {
                const value = parseInt(e.target.value);
                resolutionValue.textContent = value;
            });
            
            // 旋转速度滑块事件
            const rotationSpeedSlider = document.getElementById('rotationSpeed');
            const rotationSpeedValue = document.getElementById('rotationSpeedValue');
            
            rotationSpeedSlider.addEventListener('input', (e) => {
                rotationSpeed = parseFloat(e.target.value);
                rotationSpeedValue.textContent = rotationSpeed.toFixed(1);
            });
            
            // 重置相机按钮
            document.getElementById('resetCamera').addEventListener('click', () => {
                camera.position.set(0, 2, 3);
                controls.reset();
            });
            
            // 切换旋转按钮
            document.getElementById('toggleRotation').addEventListener('click', (e) => {
                isRotating = !isRotating;
                e.target.textContent = isRotating ? '暂停旋转' : '开始旋转';
            });
            
            // 初始创建一个示例地形
            createExampleTerrain();
        }
        
        // 页面加载完成后初始化
        window.addEventListener('load', () => {
            initThreeJS();
            initUI();
        });
    </script>
</body>
</html>